package com.gitee.fsmxm.thinking.in.concurrent.basic;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 使用 ReentrantReadWriteLock 实现： 适用于读多写少。
 * 特点： 读锁：和写锁互斥；允许多个线程同时读共享变量；
 * 写锁：和读锁/写锁互斥；
 */
public class _07_ReentrantReadWriteLock_SimpleCache {

    public static class SimpleCache<K, V> {

        private final Map<K, V> caches;
        private final ReentrantReadWriteLock.WriteLock writeLock;
        private final ReentrantReadWriteLock.ReadLock readLock;

        public SimpleCache() {
            caches = new HashMap<>();
            ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
            writeLock = lock.writeLock();
            readLock = lock.readLock();
        }

        public void put(K key, V val) {
            writeLock.lock();
            try {
                caches.put(key, val);
            } finally {
                writeLock.unlock();
            }
        }

        public V get(K key) {
            readLock.lock();
            try {
                return caches.get(key);
            } finally {
                readLock.unlock();
            }
        }

        // 如果没有缓存过才缓存 并返回缓存的值
        public V putIfAbsent(K key, V val) {
            V v = get(key);
            if (v != null) {
                return v;
            }
            writeLock.lock();
            try {
                v = caches.get(key);
                if (v != null) {
                    return v;
                }
                caches.put(key, val);
                return val;
            } finally {
                writeLock.unlock();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SimpleCache<String, String> cache = new SimpleCache<>();

        Thread t1 = new Thread(() -> {
            cache.put("1", "3");
            String s = cache.putIfAbsent("2", "6");
            System.out.println(s);
        }, "t1");
        Thread t2 = new Thread(() -> {
            String s = cache.get("3");
            System.out.println(s);
        }, "t2");
        Thread t3 = new Thread(() -> {
            String s = cache.putIfAbsent("2", "4");
            System.out.println(s);
        }, "t3");

        t1.start();
        t2.start();
        t3.start();

        t1.join();
        t2.join();
        t3.join();
        System.out.println(cache.get("1"));
        System.out.println(cache.get("2"));
    }

}
